package com.pktechan.api.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.pktechan.api.base.BaseMapper;
import com.pktechan.api.base.BaseServiceImpl;
import com.pktechan.api.base.wxPay.WXPayConstants;
import com.pktechan.api.base.wxPay.WXPayUtil;
import com.pktechan.api.dao.WxPayOrderMapper;
import com.pktechan.api.dto.WxPayOrderReq;
import com.pktechan.api.dto.WxPayOrderResp;
import com.pktechan.api.entity.TradeOrder;
import com.pktechan.api.entity.WxPayOrder;
import com.pktechan.api.enumeration.ChangeTradeOrderStatusEnum;
import com.pktechan.api.enumeration.TradeOrderStatusEnum;
import com.pktechan.api.enumeration.WxPayTradeStateEnum;
import com.pktechan.api.exception.CustomException;
import com.pktechan.api.service.*;
import com.pktechan.api.tradeOrderStatus.BaseChangeTradeOrderState;
import com.pktechan.api.tradeOrderStatus.ChangeTradeOrderState;
import com.pktechan.api.utils.CodeGenerator;
import com.pktechan.api.utils.DateUtil;
import com.pktechan.api.utils.RandomUtil;
import com.pktechan.api.utils.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service("wxPayOrderService")
@Transactional
public class WxPayOrderServiceImpl extends BaseServiceImpl<WxPayOrder> implements WxPayOrderService {

    private static final Logger logger = LoggerFactory.getLogger(WxPayOrderServiceImpl.class);

    @Autowired
    private WxPayOrderMapper wxPayOrderMapper;
    @Autowired
    private WxPayService wxPayService;
    @Autowired
    private WxPayRefundOrderService wxPayRefundOrderService;

    @Autowired
    private RedisService redisService;

    @Value("${wxpay.notify-url}")
    private String wxPayNotifyUrl;

    @Value("${wxpay.key}")
    private String wxPayKey;

    @Autowired
    private TradeOrderService tradeOrderService;


    /**
     * 由业务类实现
     *
     * @return
     */
    @Override
    public BaseMapper<WxPayOrder> getBaseMapper() {
        return wxPayOrderMapper;
    }

    /**
     * 添加微信支付订单
     *
     * @param paraMap
     * @param respMap
     * @return
     */
    @Override
    public void addWxPayOrder(Map<String, String> paraMap, Map<String, String> respMap) {
        WxPayOrder wxPayOrder = new WxPayOrder();
        wxPayOrder.setAppId(paraMap.get("appid"));
        wxPayOrder.setTradeOrderId(Integer.parseInt(paraMap.get("tradeOrderId")));
        wxPayOrder.setMchId(paraMap.get("mch_id"));
        wxPayOrder.setOpenId(paraMap.get("openid"));
        wxPayOrder.setDescription(paraMap.get("body"));
        wxPayOrder.setOutTradeNo(paraMap.get("out_trade_no"));
        wxPayOrder.setTotalFee(Integer.parseInt(paraMap.get("total_fee")));
        wxPayOrder.setTradeType(paraMap.get("trade_type"));
        wxPayOrder.setTradeState(WxPayTradeStateEnum.INITIAL.getCode());
        wxPayOrder.setTradeStateDesc(WxPayTradeStateEnum.INITIAL.getName());
        wxPayOrder.setSpbillCreateIp(paraMap.get("spbill_create_ip"));
        wxPayOrder.setTimeBegin(new Date());
        if (StringUtil.equals("SUCCESS", respMap.get("return_code")) && StringUtil.equals("SUCCESS", respMap.get("result_code"))) {
            wxPayOrder.setPrepayId(respMap.get("prepay_id"));
        }
        wxPayOrderMapper.save(wxPayOrder);
    }

    /**
     * 生成交易流水号
     *
     * @return
     */
    @Override
    public String createId() {
        String fileText = "%d{yyyyMMddHHmmss}%%parameter%";
        Map testMap = new HashMap();
        testMap.put("parameter", RandomUtil.createR(3));
        String id = CodeGenerator.generate(fileText, testMap);
        return id;
    }

    /**
     * 根据传递给微信的支付流水号查询支付订单
     *
     * @param outTradeNo
     * @return
     */
    @Override
    public WxPayOrder getByOutTradeNo(String outTradeNo) {
        return wxPayOrderMapper.getByOutTradeNo(outTradeNo);
    }

    /**
     * 更新支付结果
     *
     * @param resultMap
     * @return
     */
    @Override
    public void updateWxPayOrder(Map<String, String> resultMap) {
        WxPayOrder wxPayOrder = new WxPayOrder();
        wxPayOrder.setOpenId(resultMap.get("openid"));
        wxPayOrder.setOutTradeNo(resultMap.get("out_trade_no"));
        wxPayOrder.setTransactionId(resultMap.get("transaction_id"));
        wxPayOrder.setCashFee(Integer.parseInt(resultMap.get("cash_fee")));
        wxPayOrder.setBankType(resultMap.get("bank_type"));
        if (StringUtil.equals("SUCCESS", resultMap.get("result_code"))) {
            wxPayOrder.setTradeState(WxPayTradeStateEnum.SUCCESS.getCode());
            wxPayOrder.setTradeStateDesc(WxPayTradeStateEnum.SUCCESS.getName());
        } else {
            wxPayOrder.setTradeState(WxPayTradeStateEnum.PAY_ERROR.getCode());
            wxPayOrder.setTradeStateDesc(WxPayTradeStateEnum.PAY_ERROR.getName());
            wxPayOrder.setErrCodeDesc(resultMap.get("err_code_des"));
        }
        wxPayOrder.setTimeEnd(DateUtil.formatDateByStr(resultMap.get("time_end"), "yyyyMMddHHmmss"));
        wxPayOrderMapper.updateByWxPayOutTradeNo(wxPayOrder);
        TradeOrder tradeOrder = tradeOrderService.getByWxPayOutTradeNo(resultMap.get("out_trade_no"));
        if (TradeOrderStatusEnum.ORDERS_TO_BE_RECEIVED.getCode().equals(tradeOrder.getStatus()) || TradeOrderStatusEnum.IN_DELIVERY.getCode().equals(tradeOrder.getStatus()) || TradeOrderStatusEnum.COMPLETED.getCode().equals(tradeOrder.getStatus())) {
            return;
        }
        //支付成功。删除延时队列
        tradeOrderService.deleteTradeOrderExpireDelay(tradeOrder);
        ChangeTradeOrderState changeTradeOrderState = BaseChangeTradeOrderState.getChangeContractState(ChangeTradeOrderStatusEnum.PAY_ORDER);
        changeTradeOrderState.doChange(tradeOrder.getId(), TradeOrderStatusEnum.TO_BE_PAID.getCode(), TradeOrderStatusEnum.ORDERS_TO_BE_RECEIVED.getCode(), tradeOrder.getCustId(), ChangeTradeOrderStatusEnum.PAY_ORDER.getName());
        //如果购买的为注册配送商，则更新订单的配送商为当前客户自己,如果订单持有人与配送商相同则直接完成订单
        if (tradeOrder.getCustId().equals(tradeOrder.getDistributionCenterId())) {
            //记录操作流水
            ChangeTradeOrderState deliveryOrderChangeTradeOrderState = BaseChangeTradeOrderState.getChangeContractState(ChangeTradeOrderStatusEnum.DELIVERY_ORDER);
            deliveryOrderChangeTradeOrderState.doChange(tradeOrder.getId(), TradeOrderStatusEnum.ORDERS_TO_BE_RECEIVED.getCode(), TradeOrderStatusEnum.IN_DELIVERY.getCode(), tradeOrder.getCustId(), ChangeTradeOrderStatusEnum.DELIVERY_ORDER.getName());
            //记录操作流水
            ChangeTradeOrderState completeChangeTradeOrderState = BaseChangeTradeOrderState.getChangeContractState(ChangeTradeOrderStatusEnum.COMPLETE_ORDER);
            completeChangeTradeOrderState.doChange(tradeOrder.getId(), TradeOrderStatusEnum.IN_DELIVERY.getCode(), TradeOrderStatusEnum.COMPLETED.getCode(), tradeOrder.getCustId(), ChangeTradeOrderStatusEnum.COMPLETE_ORDER.getName());
        }
    }

    /**
     * 批量查询微信支付订单状态
     *
     * @return
     */
    @Override
    public void queryWxPayOrderList() {
        logger.debug("批量查询微信支付订单状态");
        List<WxPayOrder> wxPayOrderList = wxPayOrderMapper.getListByTradeState(WxPayTradeStateEnum.INITIAL.getCode());
        for (WxPayOrder wxPayOrder : wxPayOrderList) {
            try {
                Map<String, String> respMap = wxPayService.doOrderQuery(wxPayOrder.getOutTradeNo());
                updateWxPayOrder(respMap);
                logger.debug("查询成功。");
            } catch (Exception e) {
                e.printStackTrace();
                logger.error("批量查询微信支付订单状态失败，失败原因：" + e.getMessage());
                throw new CustomException("批量查询微信支付订单状态失败，失败原因：" + e.getMessage());
            }
        }
    }

    /**
     * 申请退款
     *
     * @param outTradeNo
     * @return
     */
    @Override
    public void refundWxPayOrder(String outTradeNo) throws Exception {
        logger.debug("微信支付订单号：" + outTradeNo + "---申请退款");
        //需要退款的微信支付订单
        WxPayOrder wxPayOrder = wxPayOrderMapper.getByOutTradeNo(outTradeNo);
        //调用微信退款接口
        Map<String, String> respMap = wxPayService.refundWxPayOrder(wxPayOrder);
        if (StringUtil.equals("SUCCESS", respMap.get("return_code")) && StringUtil.equals("SUCCESS", respMap.get("result_code"))) {
            //保存微信退款记录
            wxPayRefundOrderService.addWxPayRefundOrder(respMap);
            wxPayOrder.setTradeState(WxPayTradeStateEnum.REFUND.getCode());
            wxPayOrder.setTradeStateDesc(WxPayTradeStateEnum.REFUND.getName());
            wxPayOrderMapper.update(wxPayOrder);
        } else {
            logger.error("申请退款失败，失败原因：return_code--" + respMap.get("return_code") + "--return_msg：" + respMap.get("return_msg"));
            throw new CustomException("申请退款失败，失败原因：return_code--" + respMap.get("return_code") + "--return_msg：" + respMap.get("return_msg"));
        }
    }

    /**
     * 微信统一下单接口
     *
     * @param wxPayOrderReq
     * @return
     */
    @Override
    public WxPayOrderResp doUnifiedOrder(WxPayOrderReq wxPayOrderReq) {
        //校验支付金额与订单金额是否一致。防止金额不匹配
        TradeOrder tradeOrder = tradeOrderService.getById(wxPayOrderReq.getTradeOrderId());
        if (tradeOrder == null) {
            logger.error("订单ID：" + wxPayOrderReq.getTradeOrderId() + "---不存在");
            throw new CustomException("订单不存在");
        }

        if (!StringUtil.equals(wxPayOrderReq.getWxPayOutTradeNo(), tradeOrder.getWxPayOutTradeNo())) {
            logger.error("订单ID：" + tradeOrder.getId() + "---对应的微信支付订单号：" + tradeOrder.getWxPayOutTradeNo() + "与传递的微信支付订单号：" + wxPayOrderReq.getWxPayOutTradeNo() + "不同。");
            throw new CustomException("订单异常");
        }
        //订单金额转为分
        Integer orderAmount = tradeOrder.getAmount().multiply(new BigDecimal(100)).intValue();
        if (!orderAmount.equals(wxPayOrderReq.getTotalFee())) {
            logger.error("订单ID：" + tradeOrder.getId() + "---对应的应支付金额：" + orderAmount + "与传递的支付金额：" + wxPayOrderReq.getTotalFee() + "不同。");
            throw new CustomException("订单支付金额异常");
        }
        logger.info("请求预下单接口--------------------------------------");
        Map<String, String> paraMap = new HashMap<String, String>();
        paraMap.put("body", wxPayOrderReq.getProductDesc());
        /*防止各业务系统调用微信支付时订单号重复，现以业务系统代码（bus_sys_code）+业务系统订单号（bus_order_no）组合*/
        paraMap.put("out_trade_no", wxPayOrderReq.getWxPayOutTradeNo());
        paraMap.put("fee_type", "CNY");
        paraMap.put("total_fee", String.valueOf(wxPayOrderReq.getTotalFee()));
        paraMap.put("spbill_create_ip", wxPayOrderReq.getSpbillCreateIp());
        paraMap.put("notify_url", wxPayNotifyUrl);// 此路径是微信服务器调用支付结果通知路径
        paraMap.put("trade_type", "JSAPI");
        //获取用户登录信息
        HashMap<String, String> loginStateMap = (HashMap<String, String>) redisService.getLoginState(wxPayOrderReq.getUserLoginState());
        if (loginStateMap == null || !loginStateMap.containsKey("openid")) {
            throw new CustomException("登录超时，请关闭小程序重试");
        }
        paraMap.put("openid", loginStateMap.get("openid"));
        Map<String, String> respMap = wxPayService.doUnifiedOrder(paraMap);
        logger.info("请求预下单接口--respMap---" + JSONObject.toJSONString(respMap));
        //保存微信支付订单
        paraMap.put("tradeOrderId", wxPayOrderReq.getTradeOrderId().toString());
        addWxPayOrder(paraMap, respMap);
        // 预付商品id
        String prepay_id = "";
        if (StringUtil.equals("SUCCESS", respMap.get("return_code")) && StringUtil.equals("SUCCESS", respMap.get("result_code"))) {
            prepay_id = respMap.get("prepay_id");
        }
        logger.info("请求预下单接口---prepay_id---" + prepay_id);
        String timeStamp = String.valueOf(System.currentTimeMillis());
        String nonceStr = WXPayUtil.generateNonceStr();
        WxPayOrderResp wxPayOrderResp = new WxPayOrderResp();
        wxPayOrderResp.setAppId(respMap.get("appid"));
        wxPayOrderResp.setTimeStamp(timeStamp);
        wxPayOrderResp.setNonceStr(nonceStr);
        wxPayOrderResp.setSignType(WXPayConstants.HMACSHA256);
        wxPayOrderResp.setPrePayId(prepay_id);
        Map<String, String> payMap = new HashMap<String, String>();
        payMap.put("appId", respMap.get("appid"));
        payMap.put("timeStamp", timeStamp);
        payMap.put("nonceStr", nonceStr);
        payMap.put("signType", WXPayConstants.HMACSHA256);
        payMap.put("package", "prepay_id=" + prepay_id);
        //签名类型，默认为MD5，支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
        String paySign = null;
        try {
            paySign = WXPayUtil.generateSignature(payMap, wxPayKey, WXPayConstants.SignType.HMACSHA256);
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException("签名失败");
        }
        wxPayOrderResp.setPaySign(paySign);
        return wxPayOrderResp;
    }

    /**
     * 根据订单ID查询微信支付下单ID
     *
     * @param wxPayOrderReq
     * @return
     */
    @Override
    public WxPayOrderResp getPrepayIdByTradeOrderId(WxPayOrderReq wxPayOrderReq) {
        //校验支付金额与订单金额是否一致。防止金额不匹配
        TradeOrder tradeOrder = tradeOrderService.getById(wxPayOrderReq.getTradeOrderId());
        if (tradeOrder == null) {
            throw new CustomException("订单不存在");
        }
        WxPayOrder wxPayOrder = wxPayOrderMapper.getByOutTradeNo(tradeOrder.getWxPayOutTradeNo());
        logger.info("查询未支付订单对应的预下单ID---prepay_id---" + wxPayOrder.getPrepayId());
        String timeStamp = String.valueOf(System.currentTimeMillis());
        String nonceStr = WXPayUtil.generateNonceStr();
        WxPayOrderResp wxPayOrderResp = new WxPayOrderResp();
        wxPayOrderResp.setAppId(wxPayOrder.getAppId());
        wxPayOrderResp.setTimeStamp(timeStamp);
        wxPayOrderResp.setNonceStr(nonceStr);
        wxPayOrderResp.setSignType(WXPayConstants.HMACSHA256);
        wxPayOrderResp.setPrePayId(wxPayOrder.getPrepayId());
        Map<String, String> payMap = new HashMap<String, String>();
        payMap.put("appId", wxPayOrder.getAppId());
        payMap.put("timeStamp", timeStamp);
        payMap.put("nonceStr", nonceStr);
        payMap.put("signType", WXPayConstants.HMACSHA256);
        payMap.put("package", "prepay_id=" + wxPayOrder.getPrepayId());
        //签名类型，默认为MD5，支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
        String paySign = null;
        try {
            paySign = WXPayUtil.generateSignature(payMap, wxPayKey, WXPayConstants.SignType.HMACSHA256);
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException("签名失败");
        }
        wxPayOrderResp.setPaySign(paySign);
        return wxPayOrderResp;
    }

}
